import random
from flask import current_app
from flask import request, jsonify
from re import match

from flask import session

from info import constants, redis_store, db
# from info import redis_store
from info.models import User  # 导入redis里的user表
from info.utils.response_code import RET
from info.utils.yuntongxun.sms import CCP
from . import passport_blu
from info.utils.captcha.captcha import captcha  # 使用captcha生成图片验证码(要先导入captcha)
from flask import make_response  # 修改响应Content-Type为图片类型

@passport_blu.route('/image_code')
def image_code():  # 图片验证码
    '''
    图片步骤：
    1、拿到uuid，即image_code_id，并且校验(判空)
    2、生成图片验证码
    3、把图片信息保存到redis，即image_code_id，值是图片验证码的文字内容
    4、返回图片验证码
    '''

    # 1、拿到uuid
    image_code_id = request.args.get("image_code_id")

    # 2、校验image_code_id(判空)
    if not image_code_id:
        return jsonify(errno = RET.DATAERR, errmsg = "缺少参数")

    # 3、生成图片验证码
        # 使用captcha生成图片验证码(要先导入captcha)
        # 调用captcha的generate_captcha方法,这个方法返回三个数据，
        # 第一个是图片的名称，第二个是验证码文本，第三个是验证码图片的内容
    name, text, content = captcha.generate_captcha()

    # 4、把图片信息保存到redis，键是iamge_code_id，值是图片验证码的文字内容
    # redis_store.set(键，值，有效时间)
    try:
        # from info import constants, redis_store
        redis_store.set("IMAGE_CODE_ID_%s" % image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR, errmsg = "保存图片验证码失败")

    # 设置返回对象
    response = make_response(content)  # content是验证码图片的内容
    # 修改Content-Type为图片类型
    response.headers["Content-Type"] = "image/jpg"
    # 返回响应体
    return response


@passport_blu.route("/sms_code", methods=["POST"])
def sms_code():  # 短信验证码
    """
    短信验证码步骤：
    1、获取参数并校验(判空、手机号格式)
      参数有：
           手机号
           输入的图片验证码
           uuid，即image_code_id
    2、对比图片验证码
          2.1 通过uuid获取redis中对应的验证码文本内容
                (如果real_image_code是None，则表示已经过期)
          2.2 将输入的图片验证码和2.1拿到的文本进行比对
    3、发送短信验证码
          3.1 查询数据库，看看用户是否已经存在(根据手机来查询用户)
          3.2 发送短信内容
          3.3 保存短信验证码内容到redis(以便后面用户点击注册的时候，对比所填验证码和当前生成的验证码是否一致)
          3.4 返回发送成功的响应
    """

    # 1、获取参数并校验(判空、手机号格式)
    # 获取参数有：手机号,输入的图片验证码,uuid，即image_code_id
    data_dict = request.json  # 将前端提交的数据转换成字典
    mobile = data_dict.get("mobile")  # 用户输入的手机号
    image_code = data_dict.get('image_code')  # 用户输入的图片验证码内容
    image_code_id = data_dict.get("image_code_id")  # 前端js生成的uuid

    # 1.1校验参数(判空、手机号格式)
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno = RET.PARAMERR, errmsg = "缺少参数")
    if not match(r"^1[3578]\d{9}$", mobile):  # 手机号格式
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式不正确")

    # 2、对比图片验证码
    # 2.1通过uuid获取redis中对应的验证码文本内容
    try:
        real_image_code = redis_store.get("IMAGE_CODE_ID_%s" % image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取图片验证码失败")
    # (如果real_image_code是None，则表示已经过期)
    if not real_image_code:  # real_image_code在数据库中找不到就nore
        # 验证码已过期
        return jsonify(errno = RET.NODATA, errmsg = "验证码已过期")
    # 2.2将输入的图片验证码和2.1(真实的图片验证码)对比
    if image_code.lower() != real_image_code.decode().lower():  # lower()小写
        return jsonify(errno = RET.DATAERR, errmsg = "输入验证码错误")

    # 3、发送短信验证码
    # 3.1查询数据库，看看用户是否已经存在(根据手机来查询用户)
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据查询失败")
    if user:  # 判断用户是否存在
        return jsonify(errno=RET.DATAEXIST, errmsg="手机号码已经存在")
    # 3.2发送短信内容
    sms_code = random.randint(0, 999999)  # 六位数,不包含999999
    sms_code = "%06d" % sms_code  # 不足六位数就补0
    print(sms_code)  # 短信的验证码


    # # 云通讯平台发送验证码(开发时可注释,短信一条6分钱)
    # ccp = CCP()
    # '''
    # 注意： 测试的短信模板编号为1
    # 第一个参数是要发送的电话号码
    # 第二个参数是列表，第一个值就是短信验证码内容，第二个值是多长时间后过期，单位分钟， 5表示5分钟
    # 第三个参数是测试的短信模板的编号，写1就可以了
    # '''
    # result = ccp.send_template_sms(mobile, [sms_code, 5], 1)
    # if result != 0:   # 发送短信失败
    #     return jsonify(errno=RET.THIRDERR, errmsg="短信发送失败")


    # 3.3保存短信验证码内容到redis(以便后面用户点击注册的时候，对比所填验证码和当前生成的验证码是否一致)
    try:
        # redis_store.set(键，值，有效时间) sms_code为短信验证码
        redis_store.set("SMS_" + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        # 保存短信验证码失败
        return jsonify(errno = RET.DBERR, errmsg = "保存短信验证码失败")
    # 3.4返回发送成功的响应
    return jsonify(errno = RET.OK, errmsg = "操作成功")


@passport_blu.route("/register", methods=["POST"])  # 注册功能后端实现
def register(): # 这个函数的作用是处理点击注册按钮的请求
    '''
    步骤分析：
        1、获取参数，判空（接收的参数有：手机号、短信验证码、密码、是否同意协议）
        2、对比redis中的短信验证码和用户输入的是否一致
        3、注册本质实际上是在User表中添加一条记录，初始化user模型，设置模型数据(手机、昵称、密码)提交
        4、在session中保存登录状态(包括用户id，手机，昵称)，以便注册后直接登录
        5、返回注册成功
    '''
    # 1.获取参数和判空(手机格式依旧需要验证)
    data_dict = request.json
    mobile = data_dict.get("mobile")
    sms_code = data_dict.get("sms_code")
    password = data_dict.get("password")
    agree = data_dict.get("agree")

    if not all([mobile, sms_code, password, agree]):
        # 判断参数是否齐全
        return jsonify(errno=RET.PARAMERR, errmsg="参数不全")
    if not match(r"^1[3578]\d{9}$", mobile):
        return jsonify(errno=RET.DATAERR, errmsg="手机格式不正确")
    # 2.从redis中获取指定手机号对应的短信验证码的
    try:
        real_sms_code = redis_store.get("SMS_" + mobile)
    except Exception as e:
        current_app.logger.error(e)
        # 获取本地验证码失败
        return jsonify(errno = RET.DBERR, errmsg = "获取本地验证码失败")

    if not real_sms_code:
        # 短信验证码过期
        return jsonify(errno = RET.NODATA, errmsg = "短信验证码过期")

    # 3.对比短信验证码，用户填写的短信验证码和redis中验证码进行对比
    if sms_code != real_sms_code.decode():
        return jsonify(errno = RET.DATAERR, errmsg = "短信验证码错误")
    # 删除短信验证码
    try:
        redis_store.delete("SMS_" + mobile)
    except Exception as e:
        current_app.logger.error(e)

    # 4.初始化user模型，并设置数据并添加到数据库
    user = User()
    user.nick_name = mobile
    user.mobile = mobile
    # 对密码进行处理
    user.password = password

    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        db.session.rollback()   #
        current_app.logger.error(e)
        # 数据保存错误
        return jsonify(errno=RET.DBERR, errmsg="数据提交失败")
    # 5. 保存用户登录状态(为了注册后能够直接登录页面)
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile

    # 6. 返回注册成功
    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_blu.route("/login", methods=["POST"])   # 登录功能后端实现
def login():
    '''
    步骤分析：
    1、获取参数判空
    2、查询用户是否存在（没有注册过的用户不允许登录）
    3、校验密码是否正确
    4、保存登录状态（在session中保存登录状态）
    5、返回登录成功
    '''
    # 1. 获取参数和判断是否有值
    data_dict = request.json
    mobile = data_dict.get("mobile")
    password = data_dict.get("password")
    if not all([mobile, password]):
        # 参数不全
        return jsonify(errno=RET.PARAMERR, errmsg="参数不全")
    if not match(r"^1[3578]\d{9}$", mobile):
        return jsonify(errno = RET.PARAMERR, errmsg = "手机格式不正确")
    # 2.从数据库查询出指定的用户
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR, errmsg = "查询数据错误")
    if not user:
        return jsonify(errno = RET.NODATA, errmsg = "用户名不存在")

    # 3.校验密码
    print(user.password_hash)
    if not user.check_passowrd(password):
        return jsonify(errno = RET.DATAERR, errmsg = "密码错误!")

    # 4.保存用户登录状态
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name  # 注意，这里和注册就不同了，直接拿user的nick_name
    session["mobile"] = user.mobile

    # print(session.get('user_id'))
    # print(session.get('nick_name'))
    # print(session.get('mobile'))

    # 记录最后一次登录时间
    from datetime import datetime
    user.last_login = datetime.now()
    try:
        db.session.commit()
    except Exception as e:
        # 这里就可以不用return了，只记录这个错误就行，没有必要阻止用户登录
        current_app.logger.error(e)

    # 5.返回登录成功
    return jsonify(errno = RET.OK, errmsg = "登录成功")

@passport_blu.route("/logout")
def logout():
    """
    不保持登录状态，即删除session里面保存着的登录信息
    """
    # 步骤:
    # 1、手动删除session的值
    # 2、返回结果
    # print(session.get('user_id'))
    # print(session.get('nick_name'))
    # print(session.get('mobile'))

    session.pop('user_id')
    session.pop('nick_name')
    session.pop('mobile')
    session.pop('is_admin', None)

    # 返回结果
    return jsonify(errno = RET.OK, errmsg = "退出登录成功")
